为什么头文件要包含#ifndef...#endif这对编译预处理指令?这对编译预处理命令表示:如果#ifndef后的标识符已经定义过,则跳过中间的所有指令,直接跳到#endif。一个程序可能由很多源文件组成,每个源文件都可能调用到库中的函数,因此每个源文件都需要包含库的头文件。如果没有#ifndef...#endif这对编译预处理指令,头文件中的内容在整个程序中可能出现很多遍,将造成编译或连接错误。有了#ifndef...#endif这对编译预处理指令可以保证头文件的内容在整个程序中只出现一遍。
为什么头文件要包含#ifndef...#endif这对编译预处理指令?
避免重复编译或包含相同的内容。这对编译预处理命令表示:如果#ifndef后的标识符已经定义过,则跳过中间的所有指令,直接跳到#endif。一个程序可能由很多源文件组成,每个源文件都可能调用到库中的函数,因此每个源文件都需要包含库的头文件。如果没有#ifndef...#endif这对编译预处理指令,头文件中的内容在整个程序中可能出现很多遍,将造成编译或连接错误。有了#ifndef...#endif这对编译预处理指令可以保证头文件的内容在整个程序中只出现一遍。
#define标识符,用于替换文本
如:
#define RADIUS 5 #define PI 3.14159 #define AREAPI*RADIUS*RADIUS
带参数的宏宏定义格式:
#define宏名(参数表)替换文本
如:#defineCIRCLE_AREA (x) (PI* (x) * (x))
当程序中出现语句area = CIRCLE_AREA (4),
就会被替换成 area = (3.14159 * (4)* (4))
用define定义宏是C语言的习惯,在C++中有更好的解决方案
函数如何完成预定功能的过程。它说明了如何从输入(参数)得到输出的(返回值)的过程。
stdio:standard input and output.
system函数包含在stdlib.h头文件中。
预处理写法:#define SECONDS_PER_YEAR (60*60*24*365)UL
#include
#include “filename”:包含了一个用户自定义的库
#include
#define PI 3.14159叫做宏替换,在预编译时,会做一个查找替换,将所有出现PI的地方替换为3.14159。
条件编码:#if...#elif...#else...#endif,可以指示预处理器根据某些条件插入一些代码至程序中。#ifndef则刚好相反。
文件包含的一般格式:
文件可以是系统提供的,也可是用户编写的。
#include<文件名>系统直接到系统指定的路径支搜索文件;
#include "文件名":系统先在当前目录搜索被包含的文件,若没找到,再到系统指定的路径去搜索。
#define明示常量manifest constatn也叫符号常量;
通过预处理器可以控制编译过程,列出要替换的内容,指明要编译的代码行和影响编译器其他方面的行为。
条件编译:
#ifndef _FUN_H_ #define _FUN_H_ //... #endif
这几句属于条件编译语句,意思是如果没有 define FUN_H 就 define FUN_H ,如果之前 define 过,#ifndef 到 #endif 的代码段就不参与编译了,这样可以避免 #ifndef 到 #endif 的代码段被重复包含。在本例中,就是防止 add 和 cnt 的重复声明。FUN_H 当然也可以取其他名字,只需要确保唯一性就可以了。
为什么不直接包含 .c 文件呢? 我在 main.c 文件里直接 #include“fun.c”不更方便吗?当然,这样编译也能通过,可是以后要是又有一个模块需要用到 fun.c 中定义的函数呢?再包含一次 fun.c ?这样不就相当于 add 函数有多处定义了吗?这样在程序链接阶段就会有麻烦,或者根本无法生成可执行程序。如果包含的是头文件,那无论包含多少次,add 函数也只有一处定义,链接是不会有问题的了。(多次声明,一次定义;)
所有的预处理指令都是以#开头,包括宏定义、文件包含、条件编译;
#include 包含一个源文件,把文件中的#include扩展为正文,即把包含.h文件找到并扩展到#include所在处。 #define 定义宏; #undef 取消已定义宏; #if 如果给定条件为真,则编译下面的代码; #indef 如果宏已经定义,则编译下面的代码; #infdef 如果宏没有定义,则编译下面的代码; #elif 如果前面#if给定条件不为真,当前条件为真,则编译下面的代码; #endif 结束一个#if……#else条件编译块; #error 停止编译并显示错误信息;
预处理:文件包含、宏定义、条件编译;
C没有模板语法,其解决的办法就是使用宏。
The preprocessor directive #define can be used to create compile-time flags. You have two choices: you can simply tell the preprocessor that the flag is defined, wthout specifying a value, like
#define FLAG
or you can give it a value (which is the typical C way to define a constant), like
#define PI 3.14159
In either case, the label can now be tested by the preprocessor to see if it has been defined.
#ifdef FLAG
This will yield a true result, and the code following the #ifdef will be included in the package sent to the compiler. This inclusion stops when the preprocessor encounters the statement
#endif
or
#endif // FLAG
Any non-comment after the #endif on the same line is illegal, even though some compilers may accept it.
The #ifdef/#endif pairs may be nested within each other.
宏是一个字符串,当一个字符串被定义为一个宏之后,如果程序中使用到这个字符串,即可以使用宏名代替,从而简化程序设计,提高程序的可维护性。
在C程序中使用宏,实际上是经过了宏定义、宏调用、宏展开三个步骤。宏定义就是使用#define命令定义一个宏标识符所代表的字符串,而宏调用则是在程序中使用宏标识符代替相应的字符串,宏展开是指在开始编译源程序之前,将程序所有的宏调用使用相应的字符串来代替。
宏只做简单的字符替换。
带参宏和函数能等价吗?
不能。带参宏和函数虽然在外形上有相似之处,但二者是完全不同的,区别如下:
(1)函数调用时,要先求出实参表达式的值,然后再代入形参;而宏只做简单的字符替换,不进行运算。
(2)函数调用是在程序运行时处理的,为形参分配临时的存储单元;而宏的展开是在编译前进行,且展开时也不分配内存,不进行值的传递,更没有返回值的概念。
(3)函数中的形参和实参都必须定义数据类型,并且要求两者类型一致,如果不一致,要进行相应的类型转换;而宏不存在任何类型问题,宏名称和参数都不需要类型,仅仅是一个符号。
一般情况下,在对C语言源程序进行编译时,编译程序将编译程序中的所有代码。有时为了调试程序的需要,可能需要只编译程序的一部分,或者根据不同的应用需要产生不同的可执行程序等,这就是条件编译。条件编译有三种形式:
(1)#ifdef 标识符
程序段1
#else
程序段2
#endif
如果定义有以指定的标识符为名称的宏,则编译程序段1,否则编译程序段2。
(2)#ifndef 标识符
程序段1
#else
程序段2
#endif
如果未定义有以指定的标识符为名称的宏,则编译程序段1,否则编译程序段2。
(3)#if 表达式
程序段1
#else
程序段2
#endif
如果表达式值为真,则编译程序段1,否则编译程序段2。
在预编译时将宏名替换成字符串的过程称为“宏展开”。#define是宏定义命令。
在C程序中使用宏,实际上是经过了宏定义、宏调用、宏展开三个步骤。宏定义就是使用#define命令定义一个宏标识符所代表的字符串,而宏调用则是在程序中使用宏标识符代替相应的字符串,宏展开是指在开始编译源程序之前,将程序所有的宏调用使用相应的字符串来代替。
宏定义、宏调用与宏展开。
以下程序运行的结果为什么是29?
#include <stdio.h> #define P 3 #define F(x) P*x*x //#define F(x) ((P)*(x)*(x)) int main() { printf("%d\n",F(3+5)); getchar(); return 0; }
1 x=3+5 2 F(x)用P*x*x替换 3 p用3替换,x用3+5替换 展开为:P*x*x 展开后参数替换为:3*3+5*3+5
带参宏和函数能等价吗?
(1)函数调用时,要先求出实参表达式的值,然后再代入形参;而宏只做简单的字符替换,不进行运算。
(2)函数调用是在程序运行时处理的,为形参分配临时的存储单元;而宏的展开是在编译前进行,且展开时也不分配内存,不进行值的传递,更没有返回值的概念。
(3)函数中的形参和实参都必须定义数据类型,并且要求两者类型一致,如果不一致,要进行相应的类型转换;而宏不存在任何类型问题,宏名称和参数都不需要类型,仅仅是一个符号。
# 和 ## 运算符
# 字符串化的意思,出现在宏定义中的#是把跟在后面的参数转换成一个字符串。
当用作字符串化操作时,# 的主要作用是将宏参数不经扩展地转换成字符串常量。
宏定义参数的左右两边的空格会被忽略,参数的各个 Token 之间的多个空格会被转换成一个空格。
宏定义参数中含有需要特殊含义字符如"或\时,它们前面会自动被加上转义字符 \。
## 连接符号,把参数连在一起。
将多个 Token 连接成一个 Token。要点:
它不能是宏定义中的第一个或最后一个 Token。
前后的空格可有可无。
#ifdef __cplusplus printf("c++"); #else printf("c"); #endif
使用参数的宏定义时,一般应将宏定义字符串中的参数都用括号括起来,并且,整个字符串部分也要用括号括起来,这样才能保证在任何替代情况下,把宏定义当做一个整体看待,从而得到一个合理的计算结果。否则,宏展开后,可能会出现意想不到的结果。
注释在预处理阶段会替换为空格,多个空格会替换为一个空格。
#define SIZE(arr) ( (sizeof (arr)) / (sizeof (*arr)))
头文件的保护性定义:
#ifndef FILE_H //#if !define FILE_H #define FILE_H …… #endif
⑴ 宏名一般用大写字母表示,以便于与变量区别。
⑵ 宏定义末尾不必加分号,否则连分号一并替换。
⑶ 使用宏可提高程序通用性和易读性,减少不一致性,减少输入错误和便于修改。如数组大小常用宏定义。
⑷ 预处理是在编译之前的处理,而编译工作的任务之一就是语法检查,预处理不做语法检查。
⑸ 宏定义写在函数的花括号外边,作用域为其后的程序,通常在文件的最开头。
⑹ “字符串”中永远不包含宏,否则该宏名当字符串处理。
⑺ 宏定义不分配内存,变量定义分配内存。
define与const不同之处的一点,define可以定义常量数组的数组长度;
预处理器一般只对同一逻辑行定义有效,但如果加上反斜杠(物理行),也能一直读取下去
#define err(flag) \ if(flag) \ printf("Correctly")
在宏的使用中有两个有用的操作符,姑且叫它操作符#, ##
对于# 我们可以认为#操作符的作用是将宏参数转化为字符串。
#include <stdio.h> #define HCMP(x, y) printf(#x" is equal to " #y"? %d\n", (x) == (y)) void main() { //... int x = 100, y = 200; HCMP(x, y); getchar(); } // x is equal to y? 0
展开以后
printf("x is equal to y ? %d\n", (100) == (200));
对于##
它实现的是将本操作符两边的参数合并成为一个完整的标记,但需要注意的是,由于预处理器只负责展开,所以程序员必须自己保证这种标记的合法性,这里涉及到一些写法问题,都列出来
#define MERGE(x, y) have_define_ ## x + y #define MERGE(x, y) have_define_##x + y ... result = MERGE(1, 3);
这里首先说明,上述写法由于习惯原因,我使用第二种,但是无论哪种都无伤大雅,效果一样。上述代码展开以后是什么呢?
result = have_define_1 + 3;
在我看来,这就有点C++中模版的思想了,虽然十分原始,但是总是有了一个方向,凭借这种方法我们能够使用宏来进行相似却不同函数的调用,虽然我们可以使用函数指针数组来存储,但需要提前知晓有几个函数,并且如果要实现动态增长还需要消耗内存分配,但宏则不同。
When you #include a file, the preprocessor replaces the #include directive with the contents of the included file. The included contents are then preprocessed (along with the rest of the file), and then compiled.
The #define directive can be used to create a macro. In C++, a macro is a rule that defines how input text is converted into replacement output text.
There are two basic types of macros: object-like macros, and function-like macros.
Function-like macros act like functions, and serve a similar purpose. We will not discuss them here, because their use is generally considered dangerous, and almost anything they can do can be done by a normal function.(efficient, and generization.)
Object-like macros can be defined in one of two ways:
#define identifier
#define identifier substitution_text
The top definition has no substitution text, whereas the bottom one does. Because these are preprocessor directives (not statements), note that neither form ends with a semicolon.
Object-like macros with substitution text
When the preprocessor encounters this directive, any further occurrence of the identifier is replaced by substitution_text. The identifier is traditionally typed in all capital letters, using underscores to represent spaces.
Object-like macros without substitution text
Object-like macros can also be defined without substitution text.
For example:
#define USE_YEN
Macros of this form work like you might expect: any further occurrence of the identifier is removed and replaced by nothing!
This might seem pretty useless, and it is useless for doing text substitution. However, that’s not what this form of the directive is generally used for. We’ll discuss the uses of this form in just a moment.
Unlike object-like macros with substitution text, macros of this form are generally considered acceptable to use.
The conditional compilation preprocessor directives allow you to specify under what conditions something will or won’t compile.